package edu.cmu.arch.pipesandfilters;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import edu.cmu.arch.pipesandfilters.frame.Frame;
import edu.cmu.arch.pipesandfilters.frame.Variable;

/**
 * This class merges frames from two input streams to an output stream.  It
 * merges from two ascending time sequential lists to a single time sequential
 * list.
 * @author Steven Thomas (steventh@andrew.cmu.edu)
 *
 */
public class TimeSequentialMergeFilter extends DualInputFrameFilterFramework {

	/**
	 * Runnable method that can be used to operate the filter
	 */
	@Override	
	protected void runFilter1() {	
		//read one frame from each filter
		Frame frame1 = null;
		Frame frame2 = null;
		
		try{
		    frame1 = readFrame1();
			frame2 = readFrame2();
		    }catch(EndOfStreamException ex)
		    {
		    	System.out.println("End of stream caught");
		    }
		    catch(IOException ioe)
		    {
		    	System.out.println(this.getName() + "::runFilter1() IOException occured; Error reading frame" + ioe.getMessage());
		    }
			
			while(true)
			{
				if((Long)frame1.getValue(Variable.Time) < (Long)frame2.getValue(Variable.Time))
				{
					//write frame1 and read frame1
					try {
						
						this.writeFrame(frame1);
					
					
					}
					catch (IOException e1) {
						System.out.print( "\n" + this.getName() + "::IOException::Merge Filter Exiting;" );
					}
					try{
							
						frame1 = readFrame1();
					
					}
					catch (IOException e)
					{
						System.out.print( "\n" + this.getName() + "::IOException::Merge Filter Exiting;" );
					}
					catch (EndOfStreamException e) {
						
						
						System.out.print( "\n" + this.getName() + "::End Of StreamException::Merge Filter Exiting;" );
						readPort2ToEnd();
						break;
					}
				}
				else
				{
					try
					{
						this.writeFrame(frame2);
					}
					catch (IOException e1) 
					{
						System.out.print( "\n" + this.getName() + "::IOException::Merge Filter Exiting;" );
						
					}
					try
					{	
						frame2 = readFrame2();		
					}
					catch (IOException e) {
						System.out.print( "\n" + this.getName() + "::IOException::Merge Filter Exiting;" );
					}
					catch (EndOfStreamException e)
					{	
						//framePort2 is empty load
						//readFrame1 until empty
						readPort1ToEnd();
						break;
				    }
				}
				
			}
		
     }
	/**
	 *  Reads port1 a frame at a time and outputs the frame
	 *  - used when in port 2 is done
	 */
	private void readPort1ToEnd()
	{
		Frame frame = null;
		while(true)
		{
			try {
				frame = readFrame1();
			}
			catch (IOException e)
			{
				System.out.print( "\n" + this.getName() + "::IOException::Merge Filter Exiting;" );
				break;
			}
			catch (EndOfStreamException e) {
				break;
			}
			
			try
			{
				writeFrame(frame);
			}
			catch (IOException e)
			{	
				System.out.println(this.getName() + "::readPort1ToEnd() IOException occured; Error writing to OutPort" + e.getMessage());
				e.printStackTrace();
			}
			
		}
		
		runFilter1Finished = true;
	}
	
	
	/**
	 *  Reads port2 a frame at a time and outputs the frame
	 *  - used when in port 1 is done
	 */
	private void readPort2ToEnd()
	{
		while(true)
		{
			Frame frame;
			try
			{
				frame = readFrame2();
			}
			catch (IOException e) {
				System.out.print( "\n" + this.getName() + "::IOException::Merge Filter Exiting;" );
				break;
			} catch (EndOfStreamException e) {
				
				break;
			}
			try
			{
				writeFrame(frame);
			}
			catch (IOException e)
			{
				System.out.print( "\n" + this.getName() + "::IOException::Merge Filter Exiting;" );
				e.printStackTrace();
			}
			
		}
		runFilter1Finished = true;
	}

	/**
	/*  Filter2 needs to stay alive while filter1 is processing.
	 */
	@Override
	protected void runFilter2() {		
		 while(!runFilter1Finished)
		    {
		       try {
					sleep(5);
				} catch (InterruptedException e) {
					System.out.println(this.getName() + "::runFilter2() InterruptedException occured" + e.getMessage());
					e.printStackTrace();
				}
			}
		    
			ClosePorts();
		    System.out.println("Filter Out Port2 exiting");
		    this.runFilter2Finished = true;
		    
	}

}
